import got from 'got';
import pMap from 'p-map';
import signale from '../lib/signale';
import {isrc2deezer} from './deezer';
import type {playlistInfo, trackType} from 'd-fi-core/dist/types';

interface commonType {
  id: number;
  title: string;
  duration: number;
  premiumStreamingOnly: boolean;
  trackNumber: number;
  copyright: string;
  url: string;
  explicit: boolean;
  audioQuality: string;
  artist: {
    id: number;
    name: string;
    type: string;
  };
  album: {
    id: number;
    title: string;
    cover: string;
  };
}

interface tidalTrackType extends commonType {
  isrc: string;
  editable: boolean;
  audioQuality: string;
  album: {
    id: number;
    title: string;
    cover: string;
  };
}

interface tidalAlbumType extends commonType {
  cover: string;
  videoCover: null | string;
  upc: string;
  audioQuality: string;
}

interface tidalPlaylistType {
  uuid: string;
  title: string;
  numberOfTracks: number;
  numberOfVideos: number;
  creator: {
    id: number;
  };
  description: string;
  duration: number;
  lastUpdated: string;
  created: string;
  type: string;
  publicPlaylist: boolean;
  url: string;
  image: string;
}

interface listType {
  limit: number;
  offset: number;
  totalNumberOfItems: number;
}

interface tidalArtistTopTracksType extends listType {
  items: tidalTrackType[];
}

interface tidalAlbumsTracksType extends listType {
  items: tidalAlbumType[];
}

interface tidalPlaylistTracksType extends listType {
  items: tidalTrackType[];
}

const client = got.extend({
  prefixUrl: 'https://api.tidal.com/v1/',
  headers: {
    'user-agent': 'TIDAL/3704 CFNetwork/1220.1 Darwin/20.3.0',
    'x-tidal-token': 'i4ZDjcyhed7Mu47q',
  },
  searchParams: {limit: 500, countryCode: 'US'},
  responseType: 'json',
});

/**
 * Get a track by its id
 * @param {string} id - track id
 * @example tidal.getTrack('64975224')
 */
export const getTrack = async (id: string): Promise<tidalTrackType> => {
  const {body} = await client(`tracks/${id}`);
  return body as any;
};

/**
 * Get an album by its id
 * @param {string} id - album id
 * @example tidal.getAlbum('80216363')
 */
export const getAlbum = async (id: string): Promise<tidalAlbumType> => {
  const {body} = await client(`albums/${id}`);
  return body as any;
};

/**
 * Get album tracks by album id
 * @param {string} id - album id
 * @example tidal.getAlbumTracks('80216363')
 */
export const getAlbumTracks = async (id: string): Promise<tidalAlbumsTracksType> => {
  const {body} = await client(`albums/${id}/tracks`);
  return body as any;
};

/**
 * Get artist albums by artist id
 * @param {string} id - artist id
 * @example tidal.getArtistAlbums('3575680')
 */
export const getArtistAlbums = async (id: string): Promise<tidalAlbumsTracksType> => {
  const {body}: any = await client(`artists/${id}/albums`);
  body.items = (body as tidalAlbumsTracksType).items.filter((item) => item.artist.id.toString() === id);
  return body;
};

/**
 * Get top tracks by artist
 * @param {string} id - artist id
 * @example tidal.getArtistTopTracks('3575680')
 */
export const getArtistTopTracks = async (id: string): Promise<tidalArtistTopTracksType> => {
  const {body}: any = await client(`artists/${id}/toptracks`);
  body.items = (body as tidalArtistTopTracksType).items.filter((item) => item.artist.id.toString() === id);
  return body;
};

/**
 * Get a playlist by its uuid
 * @param {string} uuid - playlist uuid
 * @example tidal.getPlaylist('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
 */
export const getPlaylist = async (uuid: string): Promise<tidalPlaylistType> => {
  const {body} = await client(`playlists/${uuid}`);
  return body as any;
};

/**
 * Get playlist tracks by playlist uuid
 * @param {string} uuid - playlist uuid
 * @example tidal.getPlaylistTracks('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
 */
export const getPlaylistTracks = async (uuid: string): Promise<tidalPlaylistTracksType> => {
  const {body} = await client(`playlists/${uuid}/tracks`);
  return body as any;
};

/**
 * Get valid urls to album art
 * @param {string} uuid - album art uuid (can be found as cover property in album object)
 * @example tidal.albumArtToUrl('9a56f482-e9cf-46c3-bb21-82710e7854d4')
 * @returns {Object}
 */
export const albumArtToUrl = (uuid: string) => {
  const baseUrl = `https://resources.tidal.com/images/${uuid.replace(/-/g, '/')}`;
  return {
    sm: `${baseUrl}/160x160.jpg`,
    md: `${baseUrl}/320x320.jpg`,
    lg: `${baseUrl}/640x640.jpg`,
    xl: `${baseUrl}/1280x1280.jpg`,
  };
};

/**
 * Find tidal artists tracks on deezer
 * @param {string} id - artist id
 * @example tidal.artist2Deezer('3575680')
 */
export const artist2Deezer = async (id: string): Promise<trackType[]> => {
  console.log(signale.info('Fetching spotify artist tracks. Please hold on.'));
  const {items} = await getArtistTopTracks(id);
  const tracks: trackType[] = [];

  await pMap(
    items,
    async (item, index) => {
      try {
        const track = await isrc2deezer(item.title, item.isrc);
        // console.log(signale.success(`Track #${index}: ${item.name}`));
        tracks.push(track);
      } catch (err) {
        console.error(signale.error(`Track #${index}: ${item.title}`));
        console.error(signale.note(err.message));
      }
    },
    {concurrency: 25},
  );

  console.log(signale.info('Total matched tracks --> ' + tracks.length));
  return tracks;
};

/**
 * Find same set of playlist tracks on deezer
 * @param {string} uuid - playlist uuid
 * @example tidal.playlist2Deezer('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
 */
export const playlist2Deezer = async (uuid: string): Promise<[playlistInfo, trackType[]]> => {
  console.log(signale.info('Fetching tidal playlist. Please hold on.'));
  const body = await getPlaylist(uuid);
  const {items} = await getPlaylistTracks(uuid);
  const tracks: trackType[] = [];

  await pMap(
    items,
    async (item, index: number) => {
      try {
        const track = await isrc2deezer(item.title, item.isrc);
        // console.log(signale.success(`Track #${index}: ${item.track.name}`));
        track.TRACK_POSITION = index + 1;
        tracks.push(track);
      } catch (err) {
        console.error(signale.error(`Track #${index}: ${item.title}`));
        console.error(signale.note(err.message));
      }
    },
    {concurrency: 25},
  );

  const userId = body.creator.id.toString();
  const playlistInfoData: playlistInfo = {
    PLAYLIST_ID: body.uuid,
    PARENT_USERNAME: userId,
    PARENT_USER_ID: userId,
    PICTURE_TYPE: 'cover',
    PLAYLIST_PICTURE: body.image,
    TITLE: body.title,
    TYPE: '0',
    STATUS: '0',
    USER_ID: userId,
    DATE_ADD: body.created,
    DATE_MOD: body.lastUpdated,
    DATE_CREATE: body.created,
    NB_SONG: body.numberOfTracks,
    NB_FAN: 0,
    CHECKSUM: body.created,
    HAS_ARTIST_LINKED: false,
    IS_SPONSORED: false,
    IS_EDITO: false,
    __TYPE__: 'playlist',
  };

  console.log(signale.info('Total matched tracks --> ' + tracks.length));
  return [playlistInfoData, tracks];
};
